#include "codevisitor.h"
#include "../semantics/visitordata.h"
#include "opcodes.h"

void CodeVisitor::writeMainCode(ClassCode & attrs)
{
    JBFile * file=data()->currentFile();
	file->write(JVMNEW);
	file->write(attrs.MyName);
	file->write(DUP);
	file->write(INVOKESPECIAL);
	file->write(attrs.RootConstructorMethodRef);
	if (attrs.Convert!=INVALID_CONVERT)
	{
	 file->write(ALOAD);
	 file->write((unsigned char)0);
	 file->write(INVOKESTATIC);
	 file->write(attrs.Convert);
	}
	file->write(INVOKEVIRTUAL);
	file->write(attrs.RootProcMethodRef);
	if (attrs.PopBeforeExit)
		file->write(POP);
	file->write(RETURN);
}

#define ACC_PUBLIC (unsigned short)0x0001
#define ACC_STATIC (unsigned short)0x0008
#define ACC_ABSTRACT  (unsigned short)0x0400

void CodeVisitor::createRootMethod()
{
	JBFile * file=data()->currentFile();
	ClassSemantic * cl=data()->CurrentClass;
	ClassCode & data=cl->codedClass();
	
	startMethodDefinition(data.MainProc,ACC_PUBLIC | ACC_STATIC,1,false, false);
	writeMainCode(data);
	finishMethodDefinition(false,false,-1,-1);
	//printf("Code alter offset: %d\n",file->getOffset());
}


void CodeVisitor::createConstructor()
{
	JBFile * file=data()->currentFile();
	ClassSemantic * cl=data()->CurrentClass;
	ClassCode & data=cl->codedClass();

	startMethodDefinition(data.MyConstructor,ACC_PUBLIC,1,false,false);
	
	//Write code
	file->write(ALOAD);
	file->write((unsigned char)0);
	file->write(INVOKESPECIAL);
	file->write(data.ParentConstructor);
	file->write(RETURN);

	finishMethodDefinition(false,false,-1,-1);
}


void CodeVisitor::startMethodDefinition(AttrInfo & methodInf,unsigned short attrs,int maxlocal,bool  isAbstract, bool hasSignature)
{
  JBFile * file=data()->currentFile();
  ClassSemantic * cl=data()->CurrentClass;
  ClassCode & data=cl->codedClass();

  file->write((unsigned short)(attrs ));
  methodInf.write(file);
  //We have only one attribute - code, if not we should 0 or 2 (if signature)
  unsigned short attrCount=0;
  if (!isAbstract)
		attrCount+=1;
  if (hasSignature)
		attrCount+=1;
  file->write(attrCount);

  //printf("Code offset: %d\n",file->getOffset());
  if (isAbstract==false)
  {
   file->write(data.Code);
   //Reserve attribute length
   attribute_length_offset=file->getOffset(); 
   file->reserveBytes(4);
   //Write max operand stack, we do not know, how much is presented, so just write 8192
   file->write((unsigned short)0x2000);
   //Only one variable is prsented as local
   file->write((unsigned short)maxlocal);
   code_length_offset=file->getOffset();
   file->reserveBytes(4);
  }
}
void CodeVisitor::finishMethodDefinition(bool  isAbstract, bool hasSignature, unsigned short signature, unsigned short sigIndex)
{
	JBFile * file=data()->currentFile();
	if (isAbstract==false)
	{
	//Write length of code
	file->writeAt( file->getOffset()-code_length_offset-4,code_length_offset); 
	//Write empty exception and attribute tables
	file->write((unsigned short)0);
	file->write((unsigned short)0);
	//Write length of attribute
	file->writeAt( file->getOffset()-attribute_length_offset-4,attribute_length_offset); 
	}
	if (hasSignature)
	{
		file->write(signature);
		file->write((int)2);
		file->write(sigIndex);
	}
}
void CodeVisitor::postVisitClass()
{
	//Write attribute count(0)
	data()->currentFile()->write((unsigned short)0);
	if (data()->Classes->shouldPrintCode())
	{
		data()->currentFile()->print();
	}
	else
		data()->currentFile()->save();

	delete data()->currentFile();
}

#define COMMON    (unsigned short)0x0001
#define ABSTRACT  (unsigned short)0x0401
#define INTERFACE (unsigned short)0x0601

void CodeVisitor::visitClass()
{
	JBFile * file=new JBFile(data()->CurrentClass->cnode()->name());
	ClassSemantic * cl=data()->CurrentClass;
	cl->setFile( file );
	//Write constant table
	cl->constantTable()->write(file);
	//Write class atributes
	if (cl->isInterface())
		file->write(INTERFACE);
	else
	{
		if (cl->cnode()->type()==CLM_DEFERRED)
			file->write(ABSTRACT);
		else
			file->write(COMMON);
	}
	//Write name and superclass
	ClassCode & data=cl->codedClass();
	file->write(data.MyName);
	file->write(data.SuperClass);
	//Write interfaces
	file->write((unsigned short)(data.Interfaces.size()));
	for (unsigned int i=0;i<data.Interfaces.size();i++)
		file->write(data.Interfaces[i]);

	
	
}
void CodeVisitor::visitMethod()
{
  JBFile * file=data()->currentFile();
  MethodCode & code=data()->CurrentMethod->codeAttrs();
  //Compile main method
  unsigned short attr=ACC_PUBLIC;
  bool isNonAbstract=(data()->CurrentMethod->methodType() & FDT_ABSTRACT)==0;
  if (!isNonAbstract)
	  attr=attr | ACC_ABSTRACT;
  if (data()->CurrentMethod->shouldCompileMainMethod())
  {
  this->startMethodDefinition(code.CompilingMethod,attr,code.TotalLocalArguments,!isNonAbstract,code.Signature!=INVALID_CONSTANT);
  if (data()->CurrentMethod->m_routine!=NULL && isNonAbstract)
  {
   this->produceMethodCode();
   if (code.HasResult)
   {
		file->write(ALOAD);
		file->write(code.ResultIndex);
		file->write(ARETURN);
   }
   else file->write(RETURN);
  }
  this->finishMethodDefinition(!isNonAbstract,code.Signature!=INVALID_CONSTANT,code.Signature,code.SignatureDescriptor);
  }

  //Compile overriden methods
  
  for (unsigned int i=0;i<code.ReferencingMethods.size();i++)
  {
	  this->startMethodDefinition(code.ReferencingMethods[i],ACC_PUBLIC,code.FormalArgumentCount,false,code.Signature!=INVALID_CONSTANT);
	  for (unsigned char i=0;i<code.FormalArgumentCount;i++)
	  {
		  file->write(ALOAD);
		  file->write(i);
	  }
	  file->write(INVOKEVIRTUAL);
	  file->write(code.CompilingMethodRef);
	  if (code.HasResult)
		  file->write(ARETURN);
	  else
		  file->write(RETURN);
	  this->finishMethodDefinition(false,code.Signature!=INVALID_CONSTANT,code.Signature,code.SignatureDescriptor);
  }
 
}


void CodeVisitor::produceMethodCode()
{
 if (data()->CurrentMethod->routine())
	data()->CurrentMethod->routine()->produceCode(data());
}
void CodeVisitor::visitField()
{
	JBFile * file=data()->currentFile();
	FieldCode & code=data()->CurrentField->code();
	//printf("Current offset: %d\n",file->getOffset());
	file->write((unsigned short)0x0001);
	//printf("Offset after tag: %d\n",file->getOffset());
	code.FieldInfo.write(file);
	//printf("Offset after field info: %d\n",file->getOffset());
	if (code.Signature!=INVALID_CONSTANT)
	{
		//Write signature attribute
		file->write((unsigned short)1);
		file->write(code.Signature);
		file->write((int)2);
		file->write(code.SignatureInfo);
	}
	else
	{
		file->write((unsigned short)0);
	}
	//printf("Written field offset: %d\n",file->getOffset());

}


void CodeVisitor::preMethods()
{
	JBFile * file=data()->currentFile();
	//Write method count
	unsigned short methodcount=data()->CurrentClass->getCompiledMethodsCount();
	
	file->write((unsigned short)methodcount);
	
	//Create root method if we need to
	if (data()->CurrentClass->codedClass().HasMain)
		createRootMethod();
	if (data()->CurrentClass->isInterface()==false)
		createConstructor();		
}

void CodeVisitor::preFields()
{
	JBFile * file=data()->currentFile();
	//Write field count
	//printf("Before fields offset: %d\n",file->getOffset());
	file->write(data()->CurrentClass->getCompiledFieldsCount());
	//printf("Fields offset: %d\n",file->getOffset());
}
